<!DOCTYPE html>
<html lang="zh-cn">
<head>
  <meta charset="utf-8">
  <meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1">
  <title>深入理解JavaScript之执行环境和作用域链 - smartzheng</title>
  <meta name="renderer" content="webkit" />
<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1"/>

<meta http-equiv="Cache-Control" content="no-transform" />
<meta http-equiv="Cache-Control" content="no-siteapp" />

<meta name="theme-color" content="#f8f5ec" />
<meta name="msapplication-navbutton-color" content="#f8f5ec">
<meta name="apple-mobile-web-app-capable" content="yes">
<meta name="apple-mobile-web-app-status-bar-style" content="#f8f5ec">


<meta name="author" content="smartzheng" /><meta name="description" content="引言 在Java中，被花括号包起的代码具有独立的作用域，这一点与大部分语言都差不多，在理解和使用上都很简单，而JS中的作用域却相对较为复杂，例" /><meta name="keywords" content="Hugo, theme, even" />






<meta name="generator" content="Hugo 0.68.3 with theme even" />


<link rel="canonical" href="https://smartzheng.github.io/post/%E6%B7%B1%E5%85%A5%E7%90%86%E8%A7%A3javascript%E4%B9%8B%E6%89%A7%E8%A1%8C%E7%8E%AF%E5%A2%83%E5%92%8C%E4%BD%9C%E7%94%A8%E5%9F%9F%E9%93%BE/" />
<link rel="apple-touch-icon" sizes="180x180" href="/apple-touch-icon.png">
<link rel="icon" type="image/png" sizes="32x32" href="/favicon-32x32.png">
<link rel="icon" type="image/png" sizes="16x16" href="/favicon-16x16.png">
<link rel="manifest" href="/manifest.json">
<link rel="mask-icon" href="/safari-pinned-tab.svg" color="#5bbad5">


<link href="/dist/even.c2a46f00.min.css" rel="stylesheet">
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/@fancyapps/fancybox@3.1.20/dist/jquery.fancybox.min.css" integrity="sha256-7TyXnr2YU040zfSP+rEcz29ggW4j56/ujTPwjMzyqFY=" crossorigin="anonymous">


<meta property="og:title" content="深入理解JavaScript之执行环境和作用域链" />
<meta property="og:description" content="引言 在Java中，被花括号包起的代码具有独立的作用域，这一点与大部分语言都差不多，在理解和使用上都很简单，而JS中的作用域却相对较为复杂，例" />
<meta property="og:type" content="article" />
<meta property="og:url" content="https://smartzheng.github.io/post/%E6%B7%B1%E5%85%A5%E7%90%86%E8%A7%A3javascript%E4%B9%8B%E6%89%A7%E8%A1%8C%E7%8E%AF%E5%A2%83%E5%92%8C%E4%BD%9C%E7%94%A8%E5%9F%9F%E9%93%BE/" />
<meta property="article:published_time" content="2019-08-28T00:00:00+00:00" />
<meta property="article:modified_time" content="2019-08-28T00:00:00+00:00" />
<meta itemprop="name" content="深入理解JavaScript之执行环境和作用域链">
<meta itemprop="description" content="引言 在Java中，被花括号包起的代码具有独立的作用域，这一点与大部分语言都差不多，在理解和使用上都很简单，而JS中的作用域却相对较为复杂，例">
<meta itemprop="datePublished" content="2019-08-28T00:00:00&#43;00:00" />
<meta itemprop="dateModified" content="2019-08-28T00:00:00&#43;00:00" />
<meta itemprop="wordCount" content="3081">



<meta itemprop="keywords" content="JavaScript," /><meta name="twitter:card" content="summary"/>
<meta name="twitter:title" content="深入理解JavaScript之执行环境和作用域链"/>
<meta name="twitter:description" content="引言 在Java中，被花括号包起的代码具有独立的作用域，这一点与大部分语言都差不多，在理解和使用上都很简单，而JS中的作用域却相对较为复杂，例"/>

<!--[if lte IE 9]>
  <script src="https://cdnjs.cloudflare.com/ajax/libs/classlist/1.1.20170427/classList.min.js"></script>
<![endif]-->

<!--[if lt IE 9]>
  <script src="https://cdn.jsdelivr.net/npm/html5shiv@3.7.3/dist/html5shiv.min.js"></script>
  <script src="https://cdn.jsdelivr.net/npm/respond.js@1.4.2/dest/respond.min.js"></script>
<![endif]-->

</head>
<body>
  <div id="mobile-navbar" class="mobile-navbar">
  <div class="mobile-header-logo">
    <a href="/" class="logo">smartzheng</a>
  </div>
  <div class="mobile-navbar-icon">
    <span></span>
    <span></span>
    <span></span>
  </div>
</div>
<nav id="mobile-menu" class="mobile-menu slideout-menu">
  <ul class="mobile-menu-list">
    <a href="/">
        <li class="mobile-menu-item">Home</li>
      </a><a href="/post/">
        <li class="mobile-menu-item">Archives</li>
      </a><a href="/tags/">
        <li class="mobile-menu-item">Tags</li>
      </a><a href="/categories/">
        <li class="mobile-menu-item">Categories</li>
      </a>
  </ul>
</nav>
  <div class="container" id="mobile-panel">
    <header id="header" class="header">
        <div class="logo-wrapper">
  <a href="/" class="logo">smartzheng</a>
</div>

<nav class="site-navbar">
  <ul id="menu" class="menu">
    <li class="menu-item">
        <a class="menu-item-link" href="/">Home</a>
      </li><li class="menu-item">
        <a class="menu-item-link" href="/post/">Archives</a>
      </li><li class="menu-item">
        <a class="menu-item-link" href="/tags/">Tags</a>
      </li><li class="menu-item">
        <a class="menu-item-link" href="/categories/">Categories</a>
      </li>
  </ul>
</nav>
    </header>

    <main id="main" class="main">
      <div class="content-wrapper">
        <div id="content" class="content">
          <article class="post">
    
    <header class="post-header">
      <h1 class="post-title">深入理解JavaScript之执行环境和作用域链</h1>

      <div class="post-meta">
        <span class="post-time"> 2019-08-28 </span>
        <div class="post-category">
            <a href="/categories/web%E5%89%8D%E7%AB%AF/"> web前端 </a>
            </div>
        
      </div>
    </header>

    <div class="post-toc" id="post-toc">
  <h2 class="post-toc-title">文章目录</h2>
  <div class="post-toc-content always-active">
    <nav id="TableOfContents">
  <ul>
    <li>
      <ul>
        <li><a href="#引言">引言</a></li>
        <li><a href="#1-执行环境">1. 执行环境</a></li>
        <li><a href="#2作用域">2.作用域</a></li>
        <li><a href="#3作用域链">3.作用域链</a></li>
        <li><a href="#4evalwithtry-catch">4.eval、with、try catch</a></li>
        <li><a href="#5块级作用域">5.块级作用域</a></li>
      </ul>
    </li>
  </ul>
</nav>
  </div>
</div>
    <div class="post-content">
      <h3 id="引言">引言</h3>
<p><em><strong>在Java中，被花括号包起的代码具有独立的作用域，这一点与大部分语言都差不多，在理解和使用上都很简单，而JS中的作用域却相对较为复杂，例如</strong></em></p>
<div class="highlight"><div class="chroma">
<table class="lntable"><tr><td class="lntd">
<pre class="chroma"><code><span class="lnt">1
</span><span class="lnt">2
</span><span class="lnt">3
</span><span class="lnt">4
</span></code></pre></td>
<td class="lntd">
<pre class="chroma"><code class="language-javascript" data-lang="javascript"><span class="k">if</span> <span class="p">(</span><span class="kc">true</span><span class="p">)</span> <span class="p">{</span>
    <span class="nx">a</span> <span class="o">=</span> <span class="mi">0</span>
<span class="p">}</span>
<span class="nx">console</span><span class="p">.</span><span class="nx">log</span><span class="p">(</span><span class="nx">a</span><span class="p">)</span>
</code></pre></td></tr></table>
</div>
</div><p><em><strong>在非严格模式下，以上代码没有报错，打印出了数字0，即使在严格模式下，将<code>a = 0</code>改为<code>var a = 0</code>之后也能正确执行；而在类似的Java代码中肯定是编译不过的</strong></em></p>
<p><em><strong>上篇博客<a href="https://smartzheng.github.io/post/%E6%B7%B1%E5%85%A5%E7%90%86%E8%A7%A3javascript%E4%B9%8B%E4%BD%9C%E7%94%A8%E5%9F%9F%E9%97%AD%E5%8C%85/">深入理解JavaScript之作用域闭包</a>已经详细分析了闭包，而闭包是基于JS作用域的一种特殊现象，博客中也介绍了作用域的核心原理，本文将更深入全面地分析作用域这一概念</strong></em></p>
<p><strong>先来看个简单的例子</strong></p>
<div class="highlight"><div class="chroma">
<table class="lntable"><tr><td class="lntd">
<pre class="chroma"><code><span class="lnt"> 1
</span><span class="lnt"> 2
</span><span class="lnt"> 3
</span><span class="lnt"> 4
</span><span class="lnt"> 5
</span><span class="lnt"> 6
</span><span class="lnt"> 7
</span><span class="lnt"> 8
</span><span class="lnt"> 9
</span><span class="lnt">10
</span><span class="lnt">11
</span><span class="lnt">12
</span><span class="lnt">13
</span><span class="lnt">14
</span></code></pre></td>
<td class="lntd">
<pre class="chroma"><code class="language-javascript" data-lang="javascript"><span class="kd">var</span> <span class="nx">color</span> <span class="o">=</span> <span class="s2">&#34;blue&#34;</span>

<span class="kd">function</span> <span class="nx">fun1</span><span class="p">()</span> <span class="p">{</span>
    <span class="kd">var</span> <span class="nx">color</span> <span class="o">=</span> <span class="s2">&#34;red&#34;</span>
    <span class="nx">console</span><span class="p">.</span><span class="nx">log</span><span class="p">(</span><span class="nx">color</span><span class="p">)</span>
    <span class="k">return</span> <span class="kd">function</span> <span class="nx">fun2</span><span class="p">()</span> <span class="p">{</span>
        <span class="kd">var</span> <span class="nx">color</span> <span class="o">=</span> <span class="s2">&#34;purple&#34;</span>
        <span class="nx">console</span><span class="p">.</span><span class="nx">log</span><span class="p">(</span><span class="nx">color</span><span class="p">)</span>
    <span class="p">}</span>
<span class="p">}</span>

<span class="nx">console</span><span class="p">.</span><span class="nx">log</span><span class="p">(</span><span class="nx">color</span><span class="p">)</span>
<span class="kd">var</span> <span class="nx">foo</span> <span class="o">=</span> <span class="nx">fun1</span><span class="p">()</span>
<span class="nx">foo</span><span class="p">()</span>
</code></pre></td></tr></table>
</div>
</div><p>这里会先后打印出blue、red、purple，对于稍有JS基础的人来讲这是显而易见的。本文的内容即是分析产生这样执行结果背后的原理</p>
<h3 id="1-执行环境">1. 执行环境</h3>
<p>要理解作用域，需要先理解执行环境（execution context，上下文），只需要记住以下几点</p>
<ul>
<li>执行环境定义了变量或函数能访问的数据，每个执行环境都有一个与之关联的变量对象，环境中定义的所有变量和方法都保存在这个变量中</li>
<li>全局执行环境是所有执行环境中最外围的环境，在web浏览器中，它拥有全局变量window，所有的全局变量和函数都是window的属性</li>
<li>每个函数都有自己的执行环境；当代码执行到一个函数时，其执行环境就会被压入环境栈，执行完毕后出栈销毁，保存在其中的变量和函数也随之销毁（<a href="https://smartzheng.github.io/post/%E6%B7%B1%E5%85%A5%E7%90%86%E8%A7%A3javascript%E4%B9%8B%E4%BD%9C%E7%94%A8%E5%9F%9F%E9%97%AD%E5%8C%85/">闭包</a>是个例外）</li>
</ul>
<h3 id="2作用域">2.作用域</h3>
<p>通常来说，一段程序代码中所用到的名字并不总是有效/可用的，而限定这个名字的可用性的代码范围就是这个名字的作用域</p>
<p>也可以这么说：程序需要具备在变量中存储值和取出值的能力，而变量存取的范围拥有一套规则，我们可以称这组规则为作用域</p>
<h3 id="3作用域链">3.作用域链</h3>
<p>当创建一个函数时，会自动创建一个作用域链，并且其中预先包含所有外部的变量对象（链的最底层为全局变量），这个作用域链被保存在函数的[[Scope]]属性中</p>
<p>当调用该函数时，会为函数创建一个执行环境（同时创建了函数对应的活动对象，执行环境的范围也就是函数的作用域），并且复制[[Scope]]属性中的对象，作为该执行环境的作用域链；然后将函数对应的活动对象push到作用域链的前端</p>
<p>前面说了，作用域定义的是变量存取的规则或者可用性范围，下面通过例子来分析在代码执行时，是如何通过这个规则来查找变量的</p>
<div class="highlight"><div class="chroma">
<table class="lntable"><tr><td class="lntd">
<pre class="chroma"><code><span class="lnt"> 1
</span><span class="lnt"> 2
</span><span class="lnt"> 3
</span><span class="lnt"> 4
</span><span class="lnt"> 5
</span><span class="lnt"> 6
</span><span class="lnt"> 7
</span><span class="lnt"> 8
</span><span class="lnt"> 9
</span><span class="lnt">10
</span><span class="lnt">11
</span><span class="lnt">12
</span><span class="lnt">13
</span><span class="lnt">14
</span></code></pre></td>
<td class="lntd">
<pre class="chroma"><code class="language-javascript" data-lang="javascript"><span class="kd">var</span> <span class="nx">color</span> <span class="o">=</span> <span class="s2">&#34;blue&#34;</span>

<span class="kd">function</span> <span class="nx">fun1</span><span class="p">()</span> <span class="p">{</span>
    <span class="kd">var</span> <span class="nx">color</span> <span class="o">=</span> <span class="s2">&#34;red&#34;</span>
    <span class="nx">console</span><span class="p">.</span><span class="nx">log</span><span class="p">(</span><span class="nx">color</span><span class="p">)</span>
    <span class="k">return</span> <span class="kd">function</span> <span class="nx">fun2</span><span class="p">()</span> <span class="p">{</span>
        <span class="kd">var</span> <span class="nx">color</span> <span class="o">=</span> <span class="s2">&#34;purple&#34;</span>
        <span class="nx">console</span><span class="p">.</span><span class="nx">log</span><span class="p">(</span><span class="nx">color</span><span class="p">)</span>
    <span class="p">}</span>
<span class="p">}</span>

<span class="nx">console</span><span class="p">.</span><span class="nx">log</span><span class="p">(</span><span class="nx">color</span><span class="p">)</span>
<span class="kd">var</span> <span class="nx">foo</span> <span class="o">=</span> <span class="nx">fun1</span><span class="p">()</span>
<span class="nx">foo</span><span class="p">()</span>
</code></pre></td></tr></table>
</div>
</div><p>是的，就是文章开始时的那个例子，来画一个图：</p>
<!-- raw HTML omitted -->
<p><strong>首先需要谨记，函数执行在它被定义所在的执行环境中，而非它自己的执行环境中</strong></p>
<p>在上图中，作用域链1和作用域链2分别对应于fun1和fun2的作用域链，这里以fun2为例：</p>
<p>当fun2被创建时，会创建一个作用域链，它默认包含了全局变量window和外部fun1的执行环境中作用域链中的活动对象1（每一个活动对象或全局变量中都保存有自己环境中的所有变量）</p>
<p>当执行fun2时，又创建了一个活动对象2，其中保存了fun2中定义的color（“red”）和默认参数arguments</p>
<p>在执行fun2的<code>console.log(color)</code>时，会在对应的作用域链2的活动对象2中去寻找color变量，如果找不到，则去外层环境的活动对象1中找&hellip;直到window</p>
<p>这里还有一个疑问，当在window中也找不到需要的变量时，会怎么样呢？看两个例子：</p>
<div class="highlight"><div class="chroma">
<table class="lntable"><tr><td class="lntd">
<pre class="chroma"><code><span class="lnt">1
</span><span class="lnt">2
</span><span class="lnt">3
</span><span class="lnt">4
</span><span class="lnt">5
</span></code></pre></td>
<td class="lntd">
<pre class="chroma"><code class="language-javascript" data-lang="javascript"><span class="kd">function</span> <span class="nx">foo</span><span class="p">()</span> <span class="p">{</span>
	 <span class="nx">a</span> <span class="o">=</span> <span class="mi">1</span>
    <span class="nx">console</span><span class="p">.</span><span class="nx">log</span><span class="p">(</span><span class="nx">a</span><span class="p">)</span>
<span class="p">}</span>
<span class="nx">foo</span><span class="p">()</span><span class="c1">//非严格模式下打印出 1
</span></code></pre></td></tr></table>
</div>
</div><div class="highlight"><div class="chroma">
<table class="lntable"><tr><td class="lntd">
<pre class="chroma"><code><span class="lnt">1
</span><span class="lnt">2
</span><span class="lnt">3
</span><span class="lnt">4
</span></code></pre></td>
<td class="lntd">
<pre class="chroma"><code class="language-javascript" data-lang="javascript"><span class="kd">function</span> <span class="nx">foo</span><span class="p">()</span> <span class="p">{</span>
    <span class="nx">console</span><span class="p">.</span><span class="nx">log</span><span class="p">(</span><span class="nx">a</span><span class="p">)</span>
<span class="p">}</span>
<span class="nx">foo</span><span class="p">()</span><span class="c1">//ReferenceError
</span></code></pre></td></tr></table>
</div>
</div><p>在上面的两段代码中，都需要使用到变量a，但是运行的结果却不一致。这里需要理解一下RHS查询和LHS查询：</p>
<p>简单来讲RHS查询与简单地查找某个变量的值别无二致，而LHS查询则是试图找到变量的容器本身，从而可以对其赋值，当RHS查询不到变量时，会直接报错，而RHS查询不到值时，非严格模式的情况下，会自动在全局变量中创建一个（严格模式下也会报ReferenceError），很明显在上面的代码中，前者属于LHS，后者属于RHS。关于自动在全局变量中创建变量，这点可以通过下面几段代码做个实验：</p>
<div class="highlight"><div class="chroma">
<table class="lntable"><tr><td class="lntd">
<pre class="chroma"><code><span class="lnt">1
</span><span class="lnt">2
</span><span class="lnt">3
</span><span class="lnt">4
</span></code></pre></td>
<td class="lntd">
<pre class="chroma"><code class="language-javascript" data-lang="javascript"><span class="kd">function</span> <span class="nx">foo</span><span class="p">()</span> <span class="p">{</span>
	 <span class="nx">a</span> <span class="o">=</span> <span class="mi">1</span>
<span class="p">}</span>
<span class="nx">console</span><span class="p">.</span><span class="nx">log</span><span class="p">(</span><span class="nx">a</span><span class="p">)</span><span class="c1">//ReferenceError
</span></code></pre></td></tr></table>
</div>
</div><div class="highlight"><div class="chroma">
<table class="lntable"><tr><td class="lntd">
<pre class="chroma"><code><span class="lnt">1
</span><span class="lnt">2
</span><span class="lnt">3
</span><span class="lnt">4
</span><span class="lnt">5
</span></code></pre></td>
<td class="lntd">
<pre class="chroma"><code class="language-javascript" data-lang="javascript"><span class="kd">function</span> <span class="nx">foo</span><span class="p">()</span> <span class="p">{</span>
	 <span class="nx">a</span> <span class="o">=</span> <span class="mi">1</span>
<span class="p">}</span>
<span class="nx">foo</span><span class="p">()</span>
<span class="nx">console</span><span class="p">.</span><span class="nx">log</span><span class="p">(</span><span class="nx">a</span><span class="p">)</span><span class="c1">//非严格模式下打印出 1
</span></code></pre></td></tr></table>
</div>
</div><div class="highlight"><div class="chroma">
<table class="lntable"><tr><td class="lntd">
<pre class="chroma"><code><span class="lnt">1
</span><span class="lnt">2
</span><span class="lnt">3
</span><span class="lnt">4
</span><span class="lnt">5
</span></code></pre></td>
<td class="lntd">
<pre class="chroma"><code class="language-javascript" data-lang="javascript"><span class="kd">function</span> <span class="nx">foo</span><span class="p">()</span> <span class="p">{</span>
	<span class="kd">var</span> <span class="nx">a</span> <span class="o">=</span> <span class="mi">1</span>
<span class="p">}</span>
<span class="nx">foo</span><span class="p">()</span>
<span class="nx">console</span><span class="p">.</span><span class="nx">log</span><span class="p">(</span><span class="nx">a</span><span class="p">)</span><span class="c1">//ReferenceError
</span></code></pre></td></tr></table>
</div>
</div><h3 id="4evalwithtry-catch">4.eval、with、try catch</h3>
<p>除了函数，这里还有几个能够改变作用域的方法（块级作用域后面专门分析），虽然eval、with并不被推荐使用，但是最好还是有所了解</p>
<div class="highlight"><div class="chroma">
<table class="lntable"><tr><td class="lntd">
<pre class="chroma"><code><span class="lnt">1
</span><span class="lnt">2
</span><span class="lnt">3
</span><span class="lnt">4
</span><span class="lnt">5
</span><span class="lnt">6
</span></code></pre></td>
<td class="lntd">
<pre class="chroma"><code class="language-javascript" data-lang="javascript"><span class="kd">function</span> <span class="nx">foo</span><span class="p">(</span><span class="nx">str</span><span class="p">,</span> <span class="nx">a</span><span class="p">)</span> <span class="p">{</span>
    <span class="nb">eval</span><span class="p">(</span><span class="nx">str</span><span class="p">);</span> <span class="c1">// 欺骗!
</span><span class="c1"></span>    <span class="nx">console</span><span class="p">.</span><span class="nx">log</span><span class="p">(</span> <span class="nx">a</span><span class="p">,</span> <span class="nx">b</span> <span class="p">);</span>
<span class="p">}</span>
<span class="kd">var</span> <span class="nx">b</span> <span class="o">=</span> <span class="mi">2</span><span class="p">;</span>
<span class="nx">foo</span><span class="p">(</span><span class="s2">&#34;var b = 3;&#34;</span><span class="p">,</span> <span class="mi">1</span><span class="p">);</span> <span class="c1">// 1, 3
</span></code></pre></td></tr></table>
</div>
</div><p>以上代码通过eval传入<code>&quot;var b = 3;&quot;</code>，使得b成为foo作用域内的变量</p>
<div class="highlight"><div class="chroma">
<table class="lntable"><tr><td class="lntd">
<pre class="chroma"><code><span class="lnt"> 1
</span><span class="lnt"> 2
</span><span class="lnt"> 3
</span><span class="lnt"> 4
</span><span class="lnt"> 5
</span><span class="lnt"> 6
</span><span class="lnt"> 7
</span><span class="lnt"> 8
</span><span class="lnt"> 9
</span><span class="lnt">10
</span><span class="lnt">11
</span><span class="lnt">12
</span><span class="lnt">13
</span></code></pre></td>
<td class="lntd">
<pre class="chroma"><code class="language-javascript" data-lang="javascript"><span class="kd">function</span> <span class="nx">foo</span><span class="p">(</span><span class="nx">obj</span><span class="p">)</span> <span class="p">{</span>
    <span class="kd">with</span> <span class="p">(</span><span class="nx">obj</span><span class="p">)</span> <span class="p">{</span>
        <span class="nx">a</span> <span class="o">=</span> <span class="mi">1</span>
        <span class="kd">var</span> <span class="nx">b</span> <span class="o">=</span> <span class="mi">0</span>
        <span class="nx">c</span> <span class="o">=</span> <span class="mi">3</span>
    <span class="p">}</span>
    <span class="nx">console</span><span class="p">.</span><span class="nx">log</span><span class="p">(</span><span class="nx">b</span><span class="p">)</span><span class="c1">//&#34;0&#34;
</span><span class="c1"></span><span class="p">}</span>
<span class="kd">var</span> <span class="nx">obj</span> <span class="o">=</span> <span class="p">{</span> <span class="nx">a</span><span class="o">:</span> <span class="mi">2</span> <span class="p">}</span>
<span class="nx">foo</span><span class="p">(</span><span class="nx">obj</span><span class="p">)</span>
<span class="nx">console</span><span class="p">.</span><span class="nx">log</span><span class="p">(</span><span class="nx">obj</span><span class="p">.</span><span class="nx">a</span><span class="p">)</span><span class="c1">//&#34;1&#34;
</span><span class="c1"></span><span class="nx">console</span><span class="p">.</span><span class="nx">log</span><span class="p">(</span><span class="nx">c</span><span class="p">)</span><span class="c1">//&#34;3&#34;
</span><span class="c1"></span><span class="nx">console</span><span class="p">.</span><span class="nx">log</span><span class="p">(</span><span class="nx">a</span><span class="p">)</span><span class="c1">//&#34;ReferenceError&#34;
</span></code></pre></td></tr></table>
</div>
</div><p>with新建了一个作用域，似乎可以说它的参数成为该作用域的活动变量；</p>
<p>值得注意的是，在with语句中通过var声明的变量对应的作用域其实是外部方法作用域，所以例子中在with外部打印b时为3；</p>
<p>其次在上述代码中，传入with的obj没有c属性，所以在进行LHS查询时，直到全局变量都没有找到c，导致在全局变量中声明了一个c，所以<code>console.log(c)</code>的结果为3</p>
<p><strong>with和eval虽然使用简洁，但是对性能的影响却很大：在编译器进行词法分析时，会对每个变量和方法进行静态处理优化，即通过设置一个标识符预先确定它们的位置，以便在运行时能快速找到它们的位置；但是一旦使用到with和eval，由于它们会在运行期改变作用域，所以编译器无法通过静态分析来预先确定作用域，无法进行这种静态处理优化，“引擎多聪明，试图将这些悲观情况的副作用限制在最小范围内，也无法避免如果没有这些优化，代码会运行得更慢这个事实。”</strong></p>
<p>catch语句则比较简单，它会创建一个新的变量对象，其中包含的是被抛出的错误对象的声明</p>
<h3 id="5块级作用域">5.块级作用域</h3>
<p>在ES6之前，JS中没有块级作用域；而在其他类C语言中，由花括号封闭的代码块都有自己的作用域(如果用ECMAScript的话来讲，就是它们自己的执行环境)，因而支持根据条件来定义变量。例如，下面的代码在 JavaScript中并不会得到想象中的结果:</p>
<div class="highlight"><div class="chroma">
<table class="lntable"><tr><td class="lntd">
<pre class="chroma"><code><span class="lnt">1
</span><span class="lnt">2
</span><span class="lnt">3
</span><span class="lnt">4
</span></code></pre></td>
<td class="lntd">
<pre class="chroma"><code class="language-javascript" data-lang="javascript"><span class="k">if</span> <span class="p">(</span><span class="kc">true</span><span class="p">)</span> <span class="p">{</span>
    <span class="kd">var</span> <span class="nx">color</span> <span class="o">=</span> <span class="s2">&#34;blue&#34;</span><span class="p">;</span>
<span class="p">}</span>
<span class="nx">alert</span><span class="p">(</span><span class="nx">color</span><span class="p">);</span>    <span class="c1">//&#34;blue&#34;
</span></code></pre></td></tr></table>
</div>
</div><p>同样的现象还存在于for循环中：</p>
<div class="highlight"><div class="chroma">
<table class="lntable"><tr><td class="lntd">
<pre class="chroma"><code><span class="lnt">1
</span><span class="lnt">2
</span><span class="lnt">3
</span><span class="lnt">4
</span></code></pre></td>
<td class="lntd">
<pre class="chroma"><code class="language-javascript" data-lang="javascript"><span class="k">for</span> <span class="p">(</span><span class="kd">var</span> <span class="nx">i</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span> <span class="nx">i</span> <span class="o">&lt;</span> <span class="mi">10</span><span class="p">;</span> <span class="nx">i</span><span class="o">++</span><span class="p">){</span>
	<span class="nx">doSomething</span><span class="p">(</span><span class="nx">i</span><span class="p">);</span>
<span class="p">}</span>
<span class="nx">alert</span><span class="p">(</span><span class="nx">i</span><span class="p">);</span>      <span class="c1">//10
</span></code></pre></td></tr></table>
</div>
</div><p>在其他类C语言中，if语句和for循环中的i只作用于于花括号范围内，而在JS中括号内的语句则是在其外部环境中执行，定义的变量会被绑定在外部作用域(函数或全局)中，这点在我刚开始接触JS时也产生了很大的困惑，很难理解为什么要这样设计</p>
<p>不过ES6新增了let和const，解决了这个问题，可能也说明设计者也认为这算是一个缺陷吧：</p>
<div class="highlight"><div class="chroma">
<table class="lntable"><tr><td class="lntd">
<pre class="chroma"><code><span class="lnt">1
</span><span class="lnt">2
</span><span class="lnt">3
</span><span class="lnt">4
</span></code></pre></td>
<td class="lntd">
<pre class="chroma"><code class="language-javascript" data-lang="javascript"><span class="k">for</span> <span class="p">(</span><span class="kd">let</span> <span class="nx">i</span><span class="o">=</span><span class="mi">0</span><span class="p">;</span> <span class="nx">i</span> <span class="o">&lt;</span> <span class="mi">10</span><span class="p">;</span> <span class="nx">i</span><span class="o">++</span><span class="p">){</span>
	<span class="nx">doSomething</span><span class="p">(</span><span class="nx">i</span><span class="p">);</span>
<span class="p">}</span>
<span class="nx">alert</span><span class="p">(</span><span class="nx">i</span><span class="p">);</span> <span class="c1">//ReferenceError
</span></code></pre></td></tr></table>
</div>
</div><p>let关键字可以将变量绑定到所在的任意作用域中，通常是在{ }中；任意位置都可以使用 {  } ，这类似Java中的代码块，并且其中的let（或const）声明的变量都会只作用在这段括号中：</p>
<div class="highlight"><div class="chroma">
<table class="lntable"><tr><td class="lntd">
<pre class="chroma"><code><span class="lnt">1
</span><span class="lnt">2
</span><span class="lnt">3
</span><span class="lnt">4
</span></code></pre></td>
<td class="lntd">
<pre class="chroma"><code class="language-javascript" data-lang="javascript"><span class="p">{</span>
    <span class="kd">let</span> <span class="nx">bar</span> <span class="o">=</span> <span class="mi">2</span><span class="p">;</span>
<span class="p">}</span>
<span class="nx">console</span><span class="p">.</span><span class="nx">log</span><span class="p">(</span><span class="nx">bar</span><span class="p">);</span> <span class="c1">// ReferenceError! 
</span></code></pre></td></tr></table>
</div>
</div><p>关于let、const还有一个很有趣的现象，正常情况下，typeof使用在一个未声明的变量会返回undefinde：</p>
<div class="highlight"><div class="chroma">
<table class="lntable"><tr><td class="lntd">
<pre class="chroma"><code><span class="lnt">1
</span></code></pre></td>
<td class="lntd">
<pre class="chroma"><code class="language-javascript" data-lang="javascript"><span class="nx">console</span><span class="p">.</span><span class="nx">log</span><span class="p">(</span><span class="k">typeof</span> <span class="nx">a</span><span class="p">)</span><span class="c1">//undefinde
</span></code></pre></td></tr></table>
</div>
</div><p>但是修改以上代码为：</p>
<div class="highlight"><div class="chroma">
<table class="lntable"><tr><td class="lntd">
<pre class="chroma"><code><span class="lnt">1
</span><span class="lnt">2
</span></code></pre></td>
<td class="lntd">
<pre class="chroma"><code class="language-javascript" data-lang="javascript"><span class="nx">console</span><span class="p">.</span><span class="nx">log</span><span class="p">(</span><span class="k">typeof</span> <span class="nx">a</span><span class="p">)</span><span class="c1">//ReferenceError
</span><span class="c1"></span><span class="kd">let</span> <span class="nx">a</span> <span class="o">=</span> <span class="mi">1</span>
</code></pre></td></tr></table>
</div>
</div><p>这里却打印了ReferenceError，而使用var则没有问题，原因是在上面这种情况下，当使用let声明a时，在声明代码之前，a被放入了一个被称为暂时性死区的地方从而无法被应用到</p>
<blockquote>
<p>参考：<br>
《JavaScript高级程序设计》<br>
《你不知道的JavaScript》上卷<br>
《深入理解ES6》</p>
</blockquote>

    </div>

    <div class="post-copyright">
  <p class="copyright-item">
    <span class="item-title">文章作者</span>
    <span class="item-content">smartzheng</span>
  </p>
  <p class="copyright-item">
    <span class="item-title">上次更新</span>
    <span class="item-content">
        2019-08-28
        
    </span>
  </p>
  
  
</div>
<footer class="post-footer">
      <div class="post-tags">
          <a href="/tags/javascript/">JavaScript</a>
          </div>
      <nav class="post-nav">
        <a class="prev" href="/post/%E6%B7%B1%E5%85%A5%E7%90%86%E8%A7%A3javascript%E4%B9%8Bthis%E8%A7%A3%E6%9E%90/">
            <i class="iconfont icon-left"></i>
            <span class="prev-text nav-default">深入理解JavaScript之this解析</span>
            <span class="prev-text nav-mobile">上一篇</span>
          </a>
        <a class="next" href="/post/%E6%B7%B1%E5%85%A5%E7%90%86%E8%A7%A3javascript%E4%B9%8B%E4%BD%9C%E7%94%A8%E5%9F%9F%E9%97%AD%E5%8C%85/">
            <span class="next-text nav-default">深入理解JavaScript之作用域闭包</span>
            <span class="next-text nav-mobile">下一篇</span>
            <i class="iconfont icon-right"></i>
          </a>
      </nav>
    </footer>
  </article>
        </div>
        

  

  

      </div>
    </main>

    <footer id="footer" class="footer">
      <div class="social-links">
      <a href="mailto:smartzheng1993@email.com" class="iconfont icon-email" title="email"></a>
      <a href="https://github.com/smartzheng" class="iconfont icon-github" title="github"></a>
      <a href="https://weibo.com/2062336885/profile?topnav=1&amp;wvr=6" class="iconfont icon-weibo" title="weibo"></a>
      <a href="https://www.zhihu.com/people/liu-xia-hui-89-70" class="iconfont icon-zhihu" title="zhihu"></a>
      <a href="https://www.douban.com/people/smartzheng/" class="iconfont icon-douban" title="douban"></a>
  <a href="https://smartzheng.github.io/index.xml" type="application/rss+xml" class="iconfont icon-rss" title="rss"></a>
</div>

<div class="copyright">
  <span class="power-by">
    由 <a class="hexo-link" href="https://gohugo.io">Hugo</a> 强力驱动
  </span>
  <span class="division">|</span>
  <span class="theme-info">
    主题 - 
    <a class="theme-link" href="https://github.com/olOwOlo/hugo-theme-even">Even</a>
  </span>

  

  <span class="copyright-year">
    &copy; 
    2017 - 
    2020
    
  </span>
</div>
    </footer>

    <div class="back-to-top" id="back-to-top">
      <i class="iconfont icon-up"></i>
    </div>
  </div>
  
  <script src="https://cdn.jsdelivr.net/npm/jquery@3.2.1/dist/jquery.min.js" integrity="sha256-hwg4gsxgFZhOsEEamdOYGBf13FyQuiTwlAQgxVSNgt4=" crossorigin="anonymous"></script>
  <script src="https://cdn.jsdelivr.net/npm/slideout@1.0.1/dist/slideout.min.js" integrity="sha256-t+zJ/g8/KXIJMjSVQdnibt4dlaDxc9zXr/9oNPeWqdg=" crossorigin="anonymous"></script>
  <script src="https://cdn.jsdelivr.net/npm/@fancyapps/fancybox@3.1.20/dist/jquery.fancybox.min.js" integrity="sha256-XVLffZaxoWfGUEbdzuLi7pwaUJv1cecsQJQqGLe7axY=" crossorigin="anonymous"></script>
<script type="text/javascript" src="/dist/even.26188efa.min.js"></script>








</body>
</html>
